﻿namespace Filters.Demo.Core
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    using AForge.Imaging.Filters;
    using Filters.Demo.Core.FilterConfiguration;

    /// <summary>
    /// ConfigurationBuilder class
    /// </summary>
    public class ConfigurationBuilder
    {
        private readonly string fileName;
        private readonly XmlSerializer serializer = new XmlSerializer(typeof(FiltersConfiguration));

        public ConfigurationBuilder(string fileName)
        {
            this.fileName = fileName;
        }

        public void SaveFiltersConfiguration(IList<FilterElement> filters)
        {
            var writer = LoadWriterFile(this.fileName);

            this.serializer.Serialize(writer, this.GetFiltersConfiguration(filters));
        }

        public IList<FilterInfo> ReadFiltersConfiguration()
        {
            FiltersConfiguration configuration;

            try
            {
                var reader = LoadReaderFile(this.fileName);
                configuration = (FiltersConfiguration)this.serializer.Deserialize(reader);
            }
            catch (Exception)
            {
                throw new InvalidFilterFileException("Cannot load filters from XML file.");
            }

            var filterInfo = new List<FilterInfo>();
            if (configuration != null && configuration.Filters.Count > 0)
            {
                foreach (var filterData in configuration.Filters)
                {
                    filterInfo.Add(new FilterInfo { Filter = BuildFilter(filterData), FilterData = filterData });
                }
            }

            return filterInfo;
        }

        private static IFilter BuildFilter(FilterElement filterData)
        {
            var filterType = Type.GetType(filterData.Type, true);
            IFilter filter;

            if (filterData.FilterParameters.FilterParameters.Count > 0)
            {
                filter = Activator.CreateInstance(filterType, GetFilterParameters(filterData.FilterParameters.FilterParameters)) as IFilter;
            }
            else
            {
                filter = Activator.CreateInstance(filterType) as IFilter;
            }

            return filter;
        }

        private static object[] GetFilterParameters(Collection<FilterParameterElement> parameters)
        {
            var filterParameters = new List<object>(parameters.Count);
            foreach (var parameter in parameters)
            {
                if (!string.IsNullOrEmpty(parameter.Value) && parameter.FilterParameterValues.Count == 0)
                {
                    filterParameters.Add(GetSimpleParameter(parameter.Type, parameter.Value));
                }
                else
                {
                    var parameterType = Type.GetType(parameter.Type, true);
                    filterParameters.Add(Activator.CreateInstance(parameterType, GetParamterValues(parameter)));
                }
            }

            return filterParameters.ToArray();
        }

        private static object[] GetParamterValues(FilterParameterElement parameter)
        {
            var parameterValues = new List<object>();
            foreach (var parameterValue in parameter.FilterParameterValues)
            {
                parameterValues.Add(GetSimpleParameter(parameterValue.Type, parameterValue.Value));
            }

            return parameterValues.ToArray();
        }

        private static object GetSimpleParameter(string type, string value)
        {
            object simpleParamter = value;
            var isEnum = false;

            switch (type.ToLowerInvariant())
            {
                case "long":
                    simpleParamter = long.Parse(value);
                    break;
                case "int":
                    simpleParamter = int.Parse(value);
                    break;
                case "short":
                    simpleParamter = short.Parse(value);
                    break;
                case "byte":
                    simpleParamter = byte.Parse(value);
                    break;
                case "double":
                    simpleParamter = double.Parse(value);
                    break;
                case "float":
                    simpleParamter = float.Parse(value);
                    break;
                case "string":
                    simpleParamter = value;
                    break;
                default:
                    isEnum = true;
                    break;
            }

            if (!isEnum)
            {
                return simpleParamter;
            }

            var enumType = Type.GetType(type, true);
            return Enum.Parse(enumType, value);
        }

        private static XmlTextWriter LoadWriterFile(string fileName)
        {
            if (fileName == null)
            {
                throw new ArgumentNullException("fileName");
            }

            return new XmlTextWriter(fileName, Encoding.UTF8);
        }

        private static XmlTextReader LoadReaderFile(string fileName)
        {
            if (fileName == null)
            {
                throw new ArgumentNullException("fileName");
            }

            return new XmlTextReader(fileName);
        }

        private FiltersConfiguration GetFiltersConfiguration(IList<FilterElement> filters)
        {
            return new FiltersConfiguration
            {
                Filters = new Collection<FilterElement>(filters)
            };
        }
    }
}
